package gov.nasa.anml.lifted;

import java.util.*;

import gov.nasa.anml.PDDL;
import gov.nasa.anml.State;
import gov.nasa.anml.PDDL.Type;
import gov.nasa.anml.PDDL.TypeRelation;
import gov.nasa.anml.utility.SimpleString;
import gov.nasa.anml.utility.SimpleVoid;

// a symbol is an Symbol with no properties

public class SymbolType extends IdentifierImp<SimpleString,SimpleVoid> implements ExtensibleType<SymbolLiteral> {
	//extends Type<SimpleSymbol> implements Scope {

	public Enumeration<SymbolLiteral> members;

	public ArrayList<SymbolType> subTypes = new ArrayList<SymbolType>();
	public ArrayList<SymbolType> superTypes = new ArrayList<SymbolType>();
	
	public boolean open=true;
	
	public SymbolType() {
		members = new Enumeration<SymbolLiteral>();
	}
	public SymbolType(SimpleString n) {
		super(n);
		members = new Enumeration<SymbolLiteral>();
	}
	public SymbolType(SimpleString n,int i) {
		super(n,i);
		members = new Enumeration<SymbolLiteral>();
	}

	public SymbolType(Enumeration<SymbolLiteral> c, SymbolType o) {
		members = c;
		open = false;
		o.subTypes.add(this); // see ObjectType
		superTypes.add(o);
		for (SymbolLiteral l : members) {
			l.addType(this);
		}
	}

	public SymbolType(Enumeration<SymbolLiteral> c) {
		members = c;
		open = false;
		for (SymbolLiteral l : members) {
			l.addType(this);
		}
	}
	
	public void extend(ExtensibleType<SymbolLiteral> s) {
		SymbolType o = (SymbolType) s;
		o.addSubType(this);
		superTypes.add(o);
	}

	public boolean addSubType(ExtensibleType<SymbolLiteral> s) {
		SymbolType o = (SymbolType) s;
		if (open)
			subTypes.add(o);
		else {
			System.err.println("Error: trying to extend the closed type `" + this + "' with '" + s + ".");
		}
		return open;
	}
	
	public void addSuperType(ExtensibleType<SymbolLiteral> s) {
		SymbolType o = (SymbolType) s;
		superTypes.add(o);
	}
	
	public void add(SymbolLiteral m) {
		if (!open)
			System.err.println("Cannot add members to a closed type");
		else {
			members.add(m);
			m.addType(this);
		}
	}
	
	public void set(Enumeration<SymbolLiteral> n) {
		if (members != null) {
			System.err.println("Error: The members of: " + name + " are already defined.  Proceeding by overwriting.  This will likely wreak havoc (concerning semantics of the model produced).");
			for (SymbolLiteral l : members) {
				l.types.remove(this);
			}
		}
		members = n;
		open = false;
		for (SymbolLiteral l : members) {
			l.addType(this);
		}
	}
	
	public SymbolType constrain(Constraint<SymbolLiteral> c) {
		if (c == null)
			return this;
		if (c instanceof Enumeration) {
			Enumeration<SymbolLiteral> e = (Enumeration<SymbolLiteral>) c;
			if (members.containsAll(e))
				return new SymbolType(e,this);
			// FIXME: take the intersection
			// (and report an error/warning on the extra stuff in c?)
		}
		return null;
		
	}

	// a type alias.  Keep members duplicated so that changes to either
	// type propagate to both.
	// the alias can be closed though, since it is exactly equal to something
	// else (which may or may not itself be closed)
	public SymbolType clone() {
		SymbolType ret = null;
		try {
			ret = (SymbolType) super.clone();
		} catch (CloneNotSupportedException e) {
			//assert false;
		}
		
		open = false;
		return ret;
	}
	
	public final TypeCode typeCode() {
		return TypeCode.Symbol;
	}

	public IdentifierCode idCode() {
		return IdentifierCode.Type;
	}
	
	public Enumeration<SymbolLiteral> members() {
		return members;
	}
	
	
	public boolean isSubType(SymbolType t) {
		if (this == t)
			return true;
		for (int i=0; i<superTypes.size(); ++i) {
			if (superTypes.get(i).isSubType(t))
				return true;
		}
		return false;
		
	}
	
	transient PDDL.Type asPDDLType;
	public void translateDecl(PDDL pddl,Interval unit) {
		if (asPDDLType != null)
			return;
		
		if (this != Unit.symbolType) {
			int length = pddl.bufAppend(name);
			asPDDLType = pddl.new Type(pddl.bufToString());
			pddl.types.add(asPDDLType);
			pddl.bufReset(length);
			
			ArrayList<PDDL.TypeRelation> typeRelations = pddl.typeRelations;
			for (SymbolType f : superTypes) {
				if (f != Unit.symbolType)
					typeRelations.add(pddl.new TypeRelation(asPDDLType,f.asPDDLType()));
			}
		} else {
			asPDDLType = pddl.Object;
		}
		
		for (SymbolLiteral m : members) {
			// the proper call is m.translateDecl(pddl,this)
			// for any subtype of unit, in general, but
			// we aren't compiling this out as a pddlAction, so we pass up to the next container if we have to.
			// where that would matter is class-local statements, which will likely become domain-level statements,
			// and that will be incorrect.  To enforce a class-local statement requires representing the type as an action
			//  (its constructor), so that statements and constraints could be checked.
			m.translateDecl(pddl,unit);
		}
	}
	
	public PDDL.Type asPDDLType() {
		return asPDDLType;
	}

	/*
	// Scope implementation


	public SymbolTable<Type<?>> types = new SymbolTable<Type<?>>();
	public SymbolTable<Constant<?>> cFluents = new SymbolTable<Constant<?>>();
	public SymbolTable<ConstantFunction<?>> cFluentFunctions = new SymbolTable<ConstantFunction<?>>();
	public SymbolTable<Fluent<?>> fluents = new SymbolTable<Fluent<?>>();
	public SymbolTable<FluentFunction<?>> fluentFunctions = new SymbolTable<FluentFunction<?>>();
	public SymbolTable<Parameter<?>> parameters ;//= new SymbolTable<Parameter<?>>();
	public SymbolTable<Action> actions ;//= new SymbolTable<Action>(); 
	
	public ArrayList<Statement> statements = new ArrayList<Statement>(); 

	// one namespace by simple id.  Should eventually implement
	// type-signature mapping.
	public HashMap<SimpleString,SymbolLiteral> symbols = new HashMap<SimpleString,SymbolLiteral>();

	
	public SymbolLiteral addSymbol(IdentifierImp s) {
		return symbols.put(s.name,s);
	}
	
	public SymbolLiteral getSymbol(SimpleString s) {
		return symbols.get(s);
	}


	public <T extends SimpleSymbol<? super T>> Parameter<T> addParameter(Parameter<T> t) {
		symbols.put(t.name,t);
		return (Parameter<T>) parameters.put(t);
	}
	
	public <T extends SimpleSymbol<? super T>> Type<T> addType(Type<T> t) {
		symbols.put(t.name(),t);
		return (Type<T>) types.put(t);
	}

	public <T extends SimpleSymbol<? super T>> Constant<T> addConstant(Constant<T> c) {
		symbols.put(c.name,c);
		return (Constant<T>) cFluents.put(c);
	}

	public <T extends SimpleSymbol<? super T>> ConstantFunction<T> addConstantFunction(ConstantFunction<T> c) {
		symbols.put(c.name,c);
		return (ConstantFunction<T>) cFluentFunctions.put(c);
	}

	public <T extends SimpleSymbol<? super T>> Fluent<T> addFluent(Fluent<T> f) {
		symbols.put(f.name,f);
		return (Fluent<T>) fluents.put(f);
	}

	public <T extends SimpleSymbol<? super T>> FluentFunction<T> addFluentFunction(FluentFunction<T> f) {
		symbols.put(f.name,f);
		return (FluentFunction<T>) fluentFunctions.put(f);
	}

	public Action addAction(Action a) {
		symbols.put(a.name,a);
		return actions.put(a);
	}

	public SimpleString getParameterName(int p) {
		return parameters.getName(p);
	}
	
	public SimpleString getTypeName(int t) {
		return types.getName(t);
	}

	public SimpleString getConstantFluentName(int c) {
		return cFluents.getName(c);
	}
	
	public SimpleString getConstantFunctionName(int c) {
		return cFluentFunctions.getName(c);
	}

	public SimpleString getFluentName(int f) {
		return fluents.getName(f);
	}

	public SimpleString getFunctionName(int f) {
		return fluentFunctions.getName(f);
	}

	public SimpleString getActionName(int a) {
		return actions.getName(a);
	}

	public Parameter<?> getParameter(int p) {
		return parameters.get(p);
	}
	
	public Type<?> getType(int t) {
		return types.get(t);
	}

	public Constant<?> getConstant(int c) {
		return cFluents.get(c);
	}

	public ConstantFunction<?> getConstantFunction(int c) {
		return cFluentFunctions.get(c);
	}

	public Fluent<?> getFluent(int f) {
		return fluents.get(f);
	}

	public FluentFunction<?> getFluentFunction(int f) {
		return fluentFunctions.get(f);
	}

	public Action getAction(int a) {
		return actions.get(a);
	}

	public Parameter<?> getParameter(SimpleString p) {
		return parameters.get(p);
	}
	
	public Type<?> getType(SimpleString t) {
		return types.get(t);
	}

	public Constant<?> getConstant(SimpleString c) {
		return cFluents.get(c);
	}

	public ConstantFunction<?> getConstantFunction(SimpleString c) {
		return cFluentFunctions.get(c);
	}

	public Fluent<?> getFluent(SimpleString f) {
		return fluents.get(f);
	}
	
	public FluentFunction<?> getFluentFunction(SimpleString f) {
		return fluentFunctions.get(f);
	}

	public Action getAction(SimpleString a) {
		return actions.get(a);
	}
	*/
	
}
